Spring 예외 처리

@Wonyu· February 28, 2025 · 4 min read

📌 Spring 기본 예외 처리 방식

Spring Boot에서 별도의 핸들링 없이 예외가 발생하면, DispatchServlet이 /error엔드포인트로 요청을 위임하고, BasicErrorController가 실행되고, 상태 코드 500 에러가 발생한다.

이때, BasicErrorControllerErrorAttributes를 이용해 예외 정보를 가져와 JSON 또는 HTML로 응답한다.

BasicErrorController.java

@Controller  
@RequestMapping("${server.error.path:${error.path:/error}}")  
public class BasicErrorController extends AbstractErrorController {

	@RequestMapping  
	public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {  
	    HttpStatus status = getStatus(request);  
		    if (status == HttpStatus.NO_CONTENT) {  
		       return new ResponseEntity<>(status);  
		    }  
	    Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));  
	    return new ResponseEntity<>(body, status);  
	}
}

Response

{
	"timestamp": "2025-02-27T05:28:50.861+00:00",
	"status": 500,
	"error": "Internal Server Error",
	"path": "/"
}

단점

  • 사용자 친화적인 메시지를 제공할 수 없음
  • 클라이언트가 에러에 대한 핸들링을 하기 힘듦
  • 특정 예외 별 다른 응답을 제공하기 힘듦

위 단점을 해결하기 위해 Spring은 에러를 유연하게 처리할 수 있는 방법을 제공한다.


📌 @ExceptionHandler

✅ Exception 클래스 별 처리할 응답을 지정할 수 있다.
✅ 에러 응답의 payload를 자유롭게 다룰 수 있다.

MyController.java

@RestController
@RequestMapping
public class MyController {

    @GetMapping
    public String throwError() {
        throw new CustomException();
    }

	@ExceptionHandler(CustomException.class)  
	public ResponseEntity<String> handleCustomException(final CustomException exception) {  
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("bad request");  
	}
}

Response

bad request

동작 과정

1️⃣ 컨트롤러 실행 중 예외 발생 (throw new CustomException();)
2️⃣ DispatcherServlet이 예외를 감지하고 @ExceptionHandler 탐색
3️⃣ @ExceptionHandler(CustomException.class) 메서드가 실행됨
4️⃣ 예외가 ResponseEntity<String>로 변환되어 클라이언트에게 응답됨

장점

  • 사용자 친화적인 메시지를 전달할 수 있다.
  • 에러 코드나 payload를 통해 클라이언트가 에러 별 핸들링을 할 수 있다.
  • 예외 별로 다른 응답을 제공할 수 있다.

📌 Spring Filter 예외 처리

문제점

Spring Filter는 서블릿 컨테이너 영역에서 관리되지 않기 때문에 Filter에서 발생한 에러는 ExceptionHandler가 감지할 수 없다.

CustomFilter.java

@Component  
@Slf4j  
public class CustomFilter implements Filter {  
  
    @Override  
    public void doFilter(final ServletRequest request, final ServletResponse response,  
        final FilterChain chain)  
        throws IOException, ServletException {  
        throw new CustomException();  
    }  
}

Response

{
	"timestamp": "2025-02-27T05:49:57.566+00:00",
	"status": 500,
	"error": "Internal Server Error",
	"path": "/"
}

Filter에서 발생한 CustomException이 위에서 봤던 BasiErrorController로 전달된 것이다.

해결 방법

따라서 FIlter의 예외의 경우, 다음과 같이 따로 처리해줘야 한다.

CustomFilter.java

@Component  
@Slf4j  
public class CustomFilter implements Filter {  
  
    @Override  
    public void doFilter(final ServletRequest request, final ServletResponse response,  
        final FilterChain chain)  
        throws IOException, ServletException {  
        HttpServletResponse httpResponse = (HttpServletResponse) response;  
        httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "bad request");  
    }  
}

Response

{
	"timestamp": "2025-02-27T05:57:10.784+00:00",
	"status": 400,
	"error": "Bad Request",
	"path": "/"
}

동작 과정

1️⃣ Filter 내부에서 httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "bad request") 호출
2️⃣ 서블릿 컨테이너가 400 상태 코드와 메시지를 포함한 요청을 다시 /error 엔드포인트로 전달
3️⃣ Spring Boot의 BasicErrorController/error 요청을 받아 JSON 응답 생성
4️⃣ 클라이언트가 JSON 응답을 받음


📌 참고

https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
https://mangkyu.tistory.com/173

@Wonyu
Hello :) I'm Wonyu